package com.rongji.dfish.framework;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.UUID;

import com.rongji.dfish.base.Utils;
import com.rongji.dfish.base.util.XMLUtil;

/**
 * ID生成器,提供id生成的方法,目前提供32位UUID和16位以时间排序的2种编号方式
 * @author DFish Team
 *
 */
public class IdGenerator {

	/**
	 * 获取32位的UUID字符串,默认小写方式
	 * @return 32位的UUID
	 */
	public static String getUUID() {
		return getUUID(false);
	}
	
	/**
	 * 获取32位的UUID字符串
	 * @param toUpperCase 是否转大写
	 * @return 32位的UUID
	 */
	public static String getUUID(boolean toUpperCase) {
		String uuid = UUID.randomUUID().toString().replace("-", "");
		if (toUpperCase) {
			uuid = uuid.toUpperCase();
		}
		return uuid;
	}
	
	// 进制数
    private static final int RADIX = 32;
    // 存储的时间戳长度
    private static final int LENGTH_TIMESTAMP = 9;
    // 存储的系统标识码长度
    private static final int LENGTH_SYSTEM_KEY = 3;
    // 存储的计数长度
    private static final int LENGTH_INDEX = 4;
    // 生成的ID位数
    private static final int LENGTH_ID = LENGTH_TIMESTAMP + LENGTH_SYSTEM_KEY + LENGTH_INDEX;
    // 上一个时间
    private static volatile long lastTime = 0L;
    // 上一个排序号
    private static volatile long lastIndex = 0;
    // 系统标示符
    private static String systemKey = "";
    
    /**
     * 生成16位{@link #LENGTH_ID}以时间排序的ID编号
     * @return 16位的ID编号
     */
    public static String getTimeSortedId() {
    	return getTimeSortedId(false);
    }
    
    /**
     * 生成16位{@link #LENGTH_ID}以时间排序的ID编号
     * @param timestamp 1970到现在为止的毫秒数
     * @return 16位的ID编号
     */
    public static String getTimeSortedId(long timestamp) {
    	return getTimeSortedId(timestamp, false);
    }
    
    /**
     * 生成16位{@link #LENGTH_ID}以时间排序的ID编号
     * @param toUpperCase 是否转大写
     * @return 16位的ID编号
     */
    public static String getTimeSortedId(boolean toUpperCase) {
        // 1970到现在为止的毫秒数
        long timestamp = System.currentTimeMillis();
        return getTimeSortedId(timestamp, toUpperCase);
    }
    
    /**
     * 生成16位{@link #LENGTH_ID}以时间排序的ID编号
     * @param timestamp 1970到现在为止的毫秒数
     * @param toUpperCase 是否转大写
     * @return 16位的ID编号
     */
    public static String getTimeSortedId(long timestamp, boolean toUpperCase) {
    	 String timestampStr = Long.toString(timestamp, RADIX);
         StringBuilder idStr = new StringBuilder(timestampStr);
         if (timestampStr.length() != LENGTH_TIMESTAMP) {
             fixString(idStr, LENGTH_TIMESTAMP);
         }
         
         // 系统标识符
         String storeSystemKey = getStoreSystemKey();
         idStr.append(storeSystemKey);
         
         // 计数
         if (timestamp != lastTime) {
             lastTime = timestamp;
             lastIndex = 0;
         } else {
             lastIndex++;
         }
         StringBuilder storeIndex = new StringBuilder();
         storeIndex.append(Long.toString(lastIndex, RADIX));
         fixString(storeIndex, LENGTH_INDEX);
         idStr.append(storeIndex.toString());
         String id = idStr.toString();
         if (toUpperCase) { // 转大写
         	id = id.toUpperCase();
         }
         return id;
    }
    
    /**
     * 系统标识码(3位{@link #LENGTH_SYSTEM_KEY} 32进制数{@link #RADIX})
     * @return
     */
    private static String getStoreSystemKey() {
        if (Utils.isEmpty(systemKey)) {
            String xmlFilePath = "";
            try {
                // Web工程获取方式
                @SuppressWarnings("deprecation")
                String appRealPath = SystemData.getInstance().getServletInfo().getServletRealPath();
                xmlFilePath = appRealPath + "WEB-INF/config/";
            } catch (Exception e) {
                // 获取不到时,默认系统临时文件夹产生
                xmlFilePath = System.getenv("TMP") + "/";
            }
            // 文件直接使用dfish-config.xml这个文件
            String fileName = "dfish-config.xml";
            String keyProperty = "dfish.id.systemKey";
            XMLUtil xmlTools = null;
            try {
                xmlTools = new XMLUtil(xmlFilePath + fileName, 1);
                systemKey = xmlTools.getProperty(keyProperty);
                if (Utils.isEmpty(systemKey)) {
                    long maxKeyValue = 1;
                    int power = (int) Math.sqrt(RADIX);
                    for (int i=0; i<LENGTH_SYSTEM_KEY; i++) {
                        maxKeyValue = maxKeyValue << power;
                    }
                    Random random = new Random();
                    int keyValue = random.nextInt((int) maxKeyValue);
                    systemKey = Integer.toString(keyValue, RADIX);
                    StringBuilder sb = new StringBuilder();
                    sb.append(systemKey);
                    fixString(sb, LENGTH_SYSTEM_KEY);
                    systemKey = sb.toString();
                    xmlTools.setProperty(keyProperty, systemKey);
                    xmlTools.writeDoc("UTF-8");
                }
            } catch (Exception e) {
                e.printStackTrace();
                // 错误时系统标示符全部补0
                StringBuilder errorSystemKey = new StringBuilder();
                fixString(errorSystemKey, LENGTH_SYSTEM_KEY);
                systemKey = errorSystemKey.toString();
            }
        }
        return systemKey;
    }
    
    /**
     * 自动补全字符串长度，若太长则自动截掉前面的字符，保证产生的ID位数不会超出
     * @param sb 字符串
     * @param strLength 字符串长度
     */
    private static void fixString(StringBuilder sb, int strLength) {
        if (sb != null && strLength > 0) {
            String oldStr = sb.toString();
            int fixLength = strLength - sb.length();
            sb.setLength(0);
            if (fixLength < 0) {
                sb.append(oldStr.substring(-fixLength));
            } else {
                for (int i = 0; i < fixLength; i++) {
                    sb.append('0');
                }
                sb.append(oldStr);
            }
        }
    }
    
    /**
     * 16位时间排序编号格式转换工具
     * 
     * @author DFish Team
     *
     */
    public static class TimeSortedIdFormat {
    	String id;
    	long[] value=new long[3];
    	
    	public TimeSortedIdFormat(String id){
    		if (id == null || id.length() != LENGTH_ID) {
    			throw new UnsupportedOperationException("The length of this id(" + id + ") must be " + LENGTH_ID + ".");
    		}
    		this.id=id;
            int split1 = LENGTH_TIMESTAMP;
            int split2 = LENGTH_TIMESTAMP + LENGTH_SYSTEM_KEY;
            String timestampStr = id.substring(0, split1);
            value[0] = Long.parseLong(timestampStr, RADIX);
            String systemKeyStr = id.substring(split1, split2);
            value[1] = Long.parseLong(systemKeyStr, RADIX);
            String indexStr = id.substring(split2);
            value[2] = Long.parseLong(indexStr, RADIX);
    	}
    	public String getId(){
    		return id;
    	}
    	public String toString(){
    		SimpleDateFormat SDF=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z");
    		return id+"={\"time\"=\""+SDF.format(getTime())+"\",\"systemKey\"="+value[1]+",\"sequence\"="+value[2]+"}";
    	}
    	public Date getTime(){
    		return new Date(value[0]);
    	}
    	public long getSequence(){
    		return value[2];
    	}
    	public long getSystemKey(){
    		return value[1];
    	}
    }
	
}
